package web

import (
	"bytes"
	"errors"
	"fmt"
	"net/http"
	"net/http/httptest"
	"net/url"
	"testing"

	"github.com/julienschmidt/httprouter"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/suite"
	"notabug.org/ropemann/imabon/src/common"
	"notabug.org/ropemann/imabon/src/entities/post"
	"notabug.org/ropemann/imabon/src/entities/thread"
)

type WebTestSuite struct {
	suite.Suite
	board    string
	threadId string
	router   httprouter.Router
	server   httptest.Server
	tlorig   map[string]string
	tlfake   map[string]string
}

func (s *WebTestSuite) SetupSuite() {
	common.LoadConfig("../../../.env")
	s.board = "ani"
	s.threadId = "625b8273c84324aea98ce004"
	s.router = *httprouter.New()
	RegisterWebRoutes(&s.router, "../../../public/html/*.html", true)
	s.server = *httptest.NewServer(&s.router)

	s.tlorig = TL
	s.tlfake = map[string]string{
		"index":  "fake-index.html",
		"thread": "fake-thread.html",
		"board":  "fake-board.html",
	}

	// NewCaptcha = func(width int, height int, option ...captcha.SetOption) (*captcha.Data, error) {
	// 	return &captcha.Data{Text: "test"}, nil
	// }

	PersistThread = func(board string, newThread thread.Thread) (string, error) {
		if board != s.board {
			return "fuck", errors.New("fuck")
		}
		return "ok", nil
	}

	CreatePost = func(board string, thread string, newPost post.Post) (int64, error) {
		if board != s.board {
			return 0, errors.New("fuck")
		}
		return 1, nil
	}
}

func (s *WebTestSuite) TearDownTest() {
	TL = s.tlorig
}

func (s *WebTestSuite) TestRegisterWebRoutes() {
	h, p, b := s.router.Lookup("GET", "/")

	assert.NotNil(s.T(), h)
	assert.Empty(s.T(), p)
	assert.False(s.T(), b)
}

func (s *WebTestSuite) TestIndex() {
	r, err := http.Get(fmt.Sprintf("%s/", s.server.URL))
	assert.Nil(s.T(), err)
	assert.Equal(s.T(), http.StatusOK, r.StatusCode)
}

func (s *WebTestSuite) TestIndexFail() {
	TL = s.tlfake
	r, err := http.Get(fmt.Sprintf("%s/", s.server.URL))
	assert.Nil(s.T(), err)
	assert.Equal(s.T(), http.StatusInternalServerError, r.StatusCode)
}

func (s *WebTestSuite) TestBoard() {
	r, err := http.Get(fmt.Sprintf("%s/b/%s", s.server.URL, s.board))
	assert.Nil(s.T(), err)
	assert.Equal(s.T(), http.StatusOK, r.StatusCode)
}

func (s *WebTestSuite) TestBoardFail() {
	TL = s.tlfake
	r, err := http.Get(fmt.Sprintf("%s/b/%s", s.server.URL, s.board))
	assert.Nil(s.T(), err)
	assert.Equal(s.T(), http.StatusInternalServerError, r.StatusCode)
}

func (s *WebTestSuite) TestThread() {
	r, err := http.Get(fmt.Sprintf("%s/b/%s/%s", s.server.URL, s.board, s.threadId))
	assert.Nil(s.T(), err)
	assert.Equal(s.T(), http.StatusOK, r.StatusCode)
}

func (s *WebTestSuite) TestThreadFail() {
	TL = s.tlfake
	r, err := http.Get(fmt.Sprintf("%s/b/%s/%s", s.server.URL, s.board, s.threadId))
	assert.Nil(s.T(), err)
	assert.Equal(s.T(), http.StatusInternalServerError, r.StatusCode)
}

func (s *WebTestSuite) TestGenerateCaptcha() {
	r, err := http.Get(fmt.Sprintf("%s/generate-captcha", s.server.URL))
	assert.Nil(s.T(), err)
	assert.Equal(s.T(), http.StatusOK, r.StatusCode)
}

func (s *WebTestSuite) TestVerifyCaptcha() {
	rb := NewRoutesBroker("../../../public/html/*.html", true)

	recorder := httptest.NewRecorder()
	writer := FakeWriter{}
	http.SetCookie(recorder, &http.Cookie{Name: "session-name", Value: "expected"})

	request := &http.Request{Header: http.Header{"Cookie": recorder.HeaderMap["Set-Cookie"]}}
	request.Form = url.Values{}
	request.Form.Add("captchaTest", "test")

	session, _ := rb.store.Get(request, "session-name")
	session.Values["captcha"] = "test"
	session.Save(request, &writer)

	ok := rb.verifyCaptcha(request)
	assert.True(s.T(), ok)
}

func (s *WebTestSuite) TestCreateThread() {
	json := []byte(`{ "threadTitle": "title", "authorName": "name", "comment": "test comment", "captchaTest": "test" }`)
	bodyReader := bytes.NewReader(json)
	r, err := http.Post(
		fmt.Sprintf("%s/b/%s", s.server.URL, s.board),
		"application/json", bodyReader)

	assert.Nil(s.T(), err)
	assert.Equal(s.T(), r.StatusCode, http.StatusOK)
}

func (s *WebTestSuite) TestCreateThreadFail() {
	json := []byte(`{ "threadTitle": "title", "authorName": "name", "comment": "test comment", "captchaTest": "test" }`)
	bodyReader := bytes.NewReader(json)
	r, err := http.Post(
		fmt.Sprintf("%s/b/%s", s.server.URL, "ASS"),
		"application/json", bodyReader)

	assert.Nil(s.T(), err)
	assert.Equal(s.T(), r.StatusCode, http.StatusInternalServerError)
}

func (s *WebTestSuite) TestCreatePost() {
	json := []byte(`{ "threadTitle": "title", "authorName": "name", "comment": "test comment", "captchaTest": "test" }`)
	bodyReader := bytes.NewReader(json)
	r, err := http.Post(
		fmt.Sprintf("%s/b/%s/%s", s.server.URL, s.board, s.threadId),
		"application/json", bodyReader)

	assert.Nil(s.T(), err)
	assert.Equal(s.T(), r.StatusCode, http.StatusOK)
}

func (s *WebTestSuite) TestCreatePostFail() {
	json := []byte(`{ "threadTitle": "title", "authorName": "name", "comment": "test comment", "captchaTest": "test" }`)
	bodyReader := bytes.NewReader(json)
	r, err := http.Post(
		fmt.Sprintf("%s/b/%s/%s", s.server.URL, "ASSS", s.threadId),
		"application/json", bodyReader)

	assert.Nil(s.T(), err)
	assert.Equal(s.T(), r.StatusCode, http.StatusInternalServerError)
}

func TestWebTestSuite(t *testing.T) {
	suite.Run(t, new(WebTestSuite))
}

type FakeWriter struct{}

func (w *FakeWriter) Header() http.Header {
	return http.Header{}
}

func (w *FakeWriter) Write([]byte) (int, error) {
	return 0, nil
}

func (w *FakeWriter) WriteHeader(statusCode int) {
	fmt.Println("STATUS: ", statusCode)
}
